package user

import (
	"strings"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	"gddgame.cc/galaxy/common/dsl/game/stat"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/core/models"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/orm"
)

type UserBase interface {
	GetId() int
	GetOpenId() string
	SetSchema(schema *orm.Schema)
	GetStats() *stat.StatInfo
	SetStats(data *stat.StatInfo)
}

type UserData struct {
	env *game.Environment

	userModel     *orm.Model
	userItemModel *orm.Model

	userGameModel    *orm.Model
	userMessageModel *orm.Model
}

func GetUserData(env *game.Environment) (*UserData, error) {
	var err error
	data := &UserData{env: env}

	if data.userModel, err = env.GetModel(UserName); err != nil {
		return nil, err
	}
	if data.userItemModel, err = env.GetModel(UserItemName); err != nil {

	}

	if data.userGameModel, err = env.GetModel(UserGameName); err != nil {
		return nil, err
	}
	if data.userMessageModel, err = env.GetModel(UserMessageName); err != nil {
		return nil, err
	}
	return data, nil
}

func (data *UserData) CreateUser(openId string, user UserBase) error {
	if _, err := data.userModel.Insert(user); err != nil {
		return SystemExceptionDetail("用户添加失败", err)
	}
	return nil
}

func (data *UserData) FindOrCreateUser(openId string, user UserBase, create func()) (bool, error) {
	var err error
	var ok bool

	if ok, _ = data.userModel.Query().Where("=", "open_id", openId).One(user); !ok {
		// 新用户，创建用户数据
		create()
		if _, err = data.userModel.Insert(user); err != nil {
			return false, SystemExceptionDetail("用户添加失败", err)
		}
		return true, nil
	}
	return false, nil
}

func (data *UserData) FindUser(openId string, user UserBase) error {
	var err error
	var ok bool

	// 只缓存open与userId的关系
	userIdS, err := data.env.CacheString("open/"+openId, func() (string, error) {
		if ok, err = data.userModel.Query().Where("=", "open_id", openId).One(user); !ok {
			return "", EmptyExceptionDetail("USER_NOEXIST", err)
		}
		return utils.ToString(user.GetId()), nil
	})
	// 通过id去查询用户
	return data.GetUser(utils.ToInt(userIdS), user)
}

func (data *UserData) CleanUser(userId int) error {
	if err := data.env.Clean("user/" + utils.ToString(userId)); err != nil {
		return err
	}
	if err := data.env.Clean("user/item/" + utils.ToString(userId)); err != nil {
		return err
	}
	return nil
}

func (data *UserData) GetUser(userId int, user UserBase) error {
	var err error
	var ok bool

	err = data.env.Cache("user/"+utils.ToString(userId), user, func() error {
		if ok, err = data.userModel.Query().Where("=", "id", userId).One(user); !ok {
			return EmptyExceptionDetail("USER_NOEXIST", err)
		}
		return nil
	})
	user.SetSchema(data.userModel.Schema())

	return err
}

func (data *UserData) FindUsers(openIds []string, id string, updateTime *time.Time, users interface{}) error {
	var err error
	query := data.userModel.Query()

	if id != "" {
		arg := []interface{}{}
		for _, v := range openIds {
			arg = append(arg, v)
		}
		arg = append(arg, id)
		query.WhereSql("`open_id` in (?"+strings.Repeat(",?", len(openIds)-1)+") or `id` = ?", arg...)
	} else {
		query.Where("in", "open_id", openIds)
	}
	if updateTime != nil {
		query.Where(">", "update_time", updateTime)
	}
	if _, err = query.Find(users); err != nil {
		return SystemExceptionDetail("用户查找失败", err)
	}
	return nil
}

func (data *UserData) UpdateUser(user UserBase, dataMap map[string]interface{}) error {
	if len(dataMap) == 0 {
		return nil
	}
	if err := data.env.SingleClean("user/"+utils.ToString(user.GetId()), func() error {
		if _, err := data.userModel.PK(user.GetId()).Update(dataMap); err != nil {
			return SystemExceptionDetail("更新用户失败", err)
		}
		return nil
	}, false); err != nil {
		return err
	}
	return nil
}

func (data *UserData) RandomUser(user UserBase, conds []string) error {
	query := data.userModel.Query()
	if conds != nil {
		for _, cond := range conds {
			query.WhereSql(cond)
		}
	}
	exist, err := query.One(user)
	if err != nil {
		return err
	}
	if !exist {
		return SystemException("不存在")
	}
	return err
}

func (data *UserData) GetUserPublicInfo(user UserBase, language string) (*GameOtherUser, error) {
	// 账号信息，等级信息，属性信息
	u := &GameOtherUser{}

	result := data.env.GetUserStatus(user.GetOpenId())
	u.Online = true
	if value, ok := result[game.RoomKey]; ok && value != "" {
		u.RoomStatus = true
	}
	if value, ok := result[game.TeamKey]; ok && value != "" {
		u.TeamStatus = true
	}
	return u, nil
}

func (data *UserData) GetUserDetailInfo(user UserBase, language string) (*GameUser, error) {
	// 账号信息，等级信息, 道具信息
	u := &GameUser{}
	return u, nil
}

func (data *UserData) GetUserStats(context def.Context, user UserBase, stats stat.StatSave) error {
	var err error
	statInfo := user.GetStats()
	if statInfo == nil || !statInfo.Stats {
		statInfo, err = stat.NewStatInfo(stats, user.GetId())
		if err != nil {
			return err
		}
		user.SetStats(statInfo)
	}
	// 自动加载统计数据
	if statInfo.Auto {
		return statInfo.GetStats(context)
	}
	return nil
}

// 更新统计记录
func (data *UserData) UpdateStatSingle(context def.Context, user UserBase, stats stat.StatSave, key string, d int32, reset bool) error {
	if err := data.GetUserStats(context, user, stats); err != nil {
		return err
	}
	userStat := user.GetStats()
	return userStat.ChangeStats(context, key, d, reset)
}

func (data *UserData) GetUserItems(user UserBase, itemIds []int, items interface{}) error {
	var err error
	err = data.env.Cache("user/item/"+utils.ToString(user.GetId()), items, func() error {
		if _, err = data.userItemModel.Query().Where("=", "user_id", user.GetId()).Where("in", "id", itemIds).Find(items); err != nil {
			return SystemExceptionDetail("查找用户道具失败", err)
		}
		return nil
	})

	return err
}

func (data *UserData) GetUserInfo(openId string, targetId string, user UserBase, language string) (*GameOtherUser, error) {
	userOpen, err := data.env.GetOpen(targetId)
	if err != nil {
		return nil, err
	}
	if err := data.FindUser(userOpen.Id, user); err != nil {
		return nil, err
	}
	info, err := data.GetUserPublicInfo(user, language)
	if err != nil {
		return nil, err
	}
	info.BindOpen(user, userOpen)
	info.IsFriend = data.env.IsFriend(openId, []models.UserOpen{*userOpen})[0]
	return info, nil
}

func (data *UserData) GetUserDetail(openId string, user UserBase, language string) (*GameUser, error) {
	userOpen, err := data.env.GetOpen(openId)
	if err != nil {
		return nil, err
	}
	if err := data.FindUser(userOpen.Id, user); err != nil {
		return nil, err
	}
	user.SetSchema(data.userModel.Schema())
	info, err := data.GetUserDetailInfo(user, language)
	if err != nil {
		return nil, err
	}
	info.BindOpen(user, userOpen)
	return info, nil
}

func (data *UserData) GetUserList(openId string, openIds []string, users []UserBase, language string) []*GameOtherUser {
	userOpens, err := data.env.GetOpenList(openIds)
	if err != nil {
		return nil
	}
	openIndexMap := make(map[string]int, len(userOpens))
	for key := range userOpens {
		openIndexMap[userOpens[key].Id] = key
	}
	userList := make([]*GameOtherUser, len(users))
	userIndexMap := make(map[string]int, len(userOpens))
	for key := range users {
		userIndexMap[users[key].GetOpenId()] = key
	}
	friends := data.env.IsFriend(openId, userOpens)
	for index := range openIds {
		openIndex := openIndexMap[openIds[index]]
		userIndex := userIndexMap[openIds[index]]
		userList[index], _ = data.GetUserPublicInfo(users[userIndex], language)
		userList[index].BindOpen(users[userIndex], &userOpens[openIndex])
		userList[index].IsFriend = friends[openIndex]
	}
	return userList
}

func (data *UserData) GetFriendList(openId string, t *time.Time, getUsers func(openIds []string) []UserBase, language string, isFriend bool) ([]*GameOtherUser, error) {
	// 搜索用户游戏内id + 昵称
	opens, err := data.env.GetFriendList(openId, isFriend, t)
	if err != nil {
		return nil, err
	}
	if len(opens) == 0 {
		return []*GameOtherUser{}, nil
	}
	ids := make([]string, len(opens))
	openMap := make(map[string]int, len(opens))
	for key := range opens {
		ids[key] = opens[key].Id
		openMap[opens[key].Id] = key
	}
	users := getUsers(ids)
	userList := make([]*GameOtherUser, len(users))
	for key := range users {
		userList[key], _ = data.GetUserPublicInfo(users[key], language)
		userList[key].BindOpen(users[key], &opens[openMap[userList[key].UserId]])
		userList[key].IsFriend = isFriend
	}
	return userList, nil
}

func (data *UserData) GetSearchList(openId string, keyword string, getUsers func(openIds []string) []UserBase, language string) ([]*GameOtherUser, error) {
	opens, err := data.env.SearchUserList(keyword, 1, 10)
	if err != nil {
		return nil, err
	}
	if len(opens) == 0 {
		return []*GameOtherUser{}, nil
	}
	ids := make([]string, len(opens))
	openMap := make(map[string]int, len(opens))
	for key := range opens {
		ids[key] = opens[key].Id
		openMap[opens[key].Id] = key
	}
	users := getUsers(ids)
	// 如果输入的是ID匹配成功，则加入队列
	if len(users) > len(opens) {
		for _, user := range users {
			if _, ok := openMap[user.GetOpenId()]; !ok {
				open, _ := data.env.GetOpen(user.GetOpenId())
				openMap[open.Id] = len(opens)
				opens = append(opens, *open)
				break
			}
		}
	}
	friends := data.env.IsFriend(openId, opens)

	userList := make([]*GameOtherUser, len(users))
	for key := range users {
		userList[key], _ = data.GetUserPublicInfo(users[key], language)
		userList[key].BindOpen(users[key], &opens[openMap[userList[key].UserId]])
		userList[key].IsFriend = friends[openMap[userList[key].UserId]]
	}
	return userList, nil
}

func (data *UserData) GetRankList(openId string, rankKey string, number int, getUsers func(openIds []string) []UserBase, language string) ([]*RankUser, error) {
	positions, err := data.env.ListLeaderboard(data.env.ID(), rankKey, number)
	if err != nil {
		return nil, err
	}
	if len(positions) == 0 {
		return []*RankUser{}, nil
	}
	openIds := make([]string, len(positions))

	var gameUserList []*RankUser
	gameUserList = make([]*RankUser, len(positions))
	for k := range positions {
		gameUserList[k] = &RankUser{Rank: positions[k].Rank, Score: positions[k].Value}
		openIds[k] = positions[k].Key
	}
	users := getUsers(openIds)
	result := data.GetUserList(openId, openIds, users, language)

	for index := range gameUserList {
		//fmt.Println("game", result[index].UserId, positions[index].Key)
		gameUserList[index].GameOtherUser = result[index]
	}
	return gameUserList, nil
}

func (data *UserData) GetUserGame(userId int, userGameId int) (interface{}, error) {
	var userGame UserGame
	err := data.env.Cache("game/"+string(userGameId), &userGame, func() error {
		if ok, err := data.userGameModel.PK(userGameId).Where("=", "user_id", userId).One(&userGame); !ok {
			return SystemExceptionDetail("获取记录失败", err)
		}
		return nil
	})
	return &userGame.Result, err
}

func (data *UserData) InsertUserGame(userGame interface{}, expire int) error {
	id, err := data.userGameModel.Insert(userGame)
	if err != nil {
		return err
	}
	_ = data.env.SetExpire("game/"+utils.ToString(id), &userGame, expire)
	return nil
}

func (data *UserData) GetMessages(userId int, updateTime *time.Time) ([]UserMessage, error) {
	var messageList []UserMessage
	query := data.userMessageModel.Query().Where("=", "user_id", userId)
	if updateTime != nil {
		query.Where(">", "create_time", updateTime)
	}
	if _, err := query.Find(&messageList); err != nil {
		return nil, SystemExceptionDetail("查找用户消息失败", err)
	}
	return messageList, nil
}

func (data *UserData) GetMessage(userId int, messageId int) (*UserMessage, error) {
	var message UserMessage
	query := data.userMessageModel.Query().Where("=", "user_id", userId).Where("=", "id", messageId)
	if ok, err := query.One(&message); !ok {
		return nil, SystemExceptionDetail("查找用户消息失败", err)
	}
	return &message, nil
}

func (data *UserData) ReadMessage(userId int, messageId int) error {
	if _, err := data.userMessageModel.PK(messageId).Where("=", "user_id", userId).Update(map[string]interface{}{
		"status": 1,
	}); err != nil {
		return SystemExceptionDetail("读取消息失败", err)
	}
	return nil
}

func (data *UserData) SendMessage(userMessage *UserMessage) error {
	if _, err := data.userMessageModel.Insert(userMessage); err != nil {
		return SystemExceptionDetail("添加消息失败", err)
	}
	return nil
}
